package fr.umlv.ig.adapter;

import java.util.ArrayList;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import fr.umlv.ig.event.StationEvent;
import fr.umlv.ig.listener.StationAdapter;
import fr.umlv.ig.model.Line;
import fr.umlv.ig.model.MetroModelImpl;
import fr.umlv.ig.model.Station;

/**
 * Adapter with a {@link MetroModelImpl}.
 * @author jenseric
 *
 */
public class StationTreeModel implements TreeModel {
	/**
	 * The {@link MetroModelImpl} of the adapter.
	 */
	private final MetroModelImpl model;
	
	/**
	 * The root of the Tree.
	 */
	private final DefaultMutableTreeNode root = new DefaultMutableTreeNode();

	/**
	 * List of listeners which listen the {@link StationTreeModel}.
	 */
	private ArrayList<TreeModelListener> listeners=new ArrayList<TreeModelListener>();
	
	/**
	 * Used for the listeners.
	 */
	boolean firing=false;
	
	/**
	 * Initializes a {@link StationTreeModel}
	 * @param model the MetroModelImpl 
	 */
	public StationTreeModel(final MetroModelImpl model) {
		this.model = model;
		model.addStationListener(new StationAdapter() {
			
			@Override
			public void stationChange(StationEvent event) {
				someNodesHaveChanged((Station) event.getSource());
			}
			
		});
		
	}
	
	
	/**
	 * Calls when a node of the tree calls.
	 * @param station the Station concerned by the changed.
	 */
	protected void someNodesHaveChanged(Station station) {
		fireTreeNodeChanged(this,new Object[] {root,model.getLine(station)},null,new Object[] {station});
	}

	
	/**
	 * Prevents every listeners of a change node.
	 * @param source the source of the change.
	 * @param path the path of the source of the change in the Tree.
	 * @param childIndices the chilINdices concerned.
	 * @param children the children concerned.
	 */
	protected void fireTreeNodeChanged(Object source, Object[] path, int[] childIndices, Object[] children) {
		firing=true;
		try {
			TreeModelEvent event=new TreeModelEvent(source,path,childIndices, children);
			for (TreeModelListener l:listeners) {
				l.treeStructureChanged(event);
			}
		} finally {
			firing=false;
		}
	}

	

	@Override
	public Object getRoot() {
		return root;
	}


	@Override
	public Object getChild(Object parent, int index) {
		if(parent == root) {
			return model.getLine(index);
		}
		if(parent instanceof Line) {
			return model.getStation((Line) parent, index);
		}
		throw new AssertionError("getChild is not supported to be invoked on a leaf");
	}


	@Override
	public int getChildCount(Object parent) {
		if(parent == root) {
			return model.getCountLine();
		}
		if(parent instanceof Line) {
			return model.getCountStation((Line)parent);
		}
		throw new AssertionError("getChildCount is not supposed to be invoked on a leaf");
	}


	@Override
	public boolean isLeaf(Object node) {
		if(node==root || node instanceof Line) {
			return false;
		}
		return true;
	}


	@Override
	public void valueForPathChanged(TreePath path, Object newValue) {
		throw new UnsupportedOperationException();

	}


	@Override
	public int getIndexOfChild(Object parent, Object child) {
		if(parent == root) {
			int n = model.getCountLine();
			for(int i=0; i< n ; i++) {
				Line line = model.getLine(i);
				if(line.equals(child)) {
					return i;
				}
			}
		}
		if(parent instanceof Line) {
			Line line = (Line) parent;
			int n= model.getCountStation(((Line) parent));
			for(int i = 0; i< n;i++) {
				if(((Station)child).equals(model.getStation(line, i))) {
					return i;
				}
			}
		}
		throw new AssertionError("getIndexOfChild is not supposed to be invoked on a leaf");
	}


	@Override
	public void addTreeModelListener(TreeModelListener l) {
		 listeners.add(l);

	}

	
	@Override
	public void removeTreeModelListener(TreeModelListener l) {
        if (firing)
            throw new IllegalStateException("Cannot remove listeners while fire... is using them");
        listeners.remove(l);
	}
	
	/**
	 * Returns true if the model is open else false.
	 * @param station the station to checked.
	 * @return true if the model is open else false.
	 */
	public boolean isOpened(Station station) {
		return model.isOpened(station);
	}


	/**
	 * Selects the line in parameter.
	 * @param line the line to select.
	 */
	public void selectedLine(Line line) {
		model.selectedLine(line);
		
	}


	
	public void selectedStation(Station station) {
		model.selectedStation(station);
		
	}


	public void closeStation(Station station) {
		model.closeStation(station);
	}


	public void openStation(Station station) {
		model.openStation(station);
	}



	public void unselected(Object o) {
		model.unselected(o);
	}



	public void selected(Object o) {
		model.selected(o);
		
	}
	
	

}
